package edu.uoc.vmerinom.tfc.service;

import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Transactional;
import org.jboss.seam.log.Log;

import edu.uoc.vmerinom.tfc.domain.Casilla;
import edu.uoc.vmerinom.tfc.domain.Grupo;
import edu.uoc.vmerinom.tfc.domain.Hora;

/**
 * Clase de servicios con las operaciones de acceso a base de datos para el
 * módulo de horario.
 * 
 * @author Víctor Manuel Merino Martínez
 * 
 */
@Name("horarioService")
@Scope(ScopeType.APPLICATION)
public class HorarioService {
	/**
	 * Gestor de entidades.
	 */
	@In(create = true)
	EntityManager entityManager;

	/**
	 * Log para la trazabilidad de errores.
	 */
	@Logger
	Log log;

	/**
	 * Obtiene el listado de horas ordenado por hora de entrada.
	 * 
	 * @return Lista de horas
	 */
	@SuppressWarnings("unchecked")
	public List<Hora> obtenerHoras() {
		if (log.isDebugEnabled()) {
			log.debug("HorarioService.obtenerHoras()");
		}
		final String query = "from Hora h order by h.horaEntrada, h.minutoEntrada, h.horaSalida, h.minutoSalida";
		log.info(query);
		return entityManager.createQuery(query).getResultList();
	}

	/**
	 * Obtiene el listado de horas activas a fecha de hoy ordenado por hora de
	 * entrada.
	 * 
	 * @return Lista de horas
	 */
	@SuppressWarnings("unchecked")
	public List<Hora> obtenerHorasActivas() {
		if (log.isDebugEnabled()) {
			log.debug("HorarioService.obtenerHorasActivas()");
		}
		String query = "from Hora h where h.baja is null or h.baja > :currentDate order by h.horaEntrada, h.minutoEntrada, h.horaSalida, h.minutoSalida";
		log.info(query);
		return entityManager.createQuery(query).setParameter("currentDate", new Date()).getResultList();
	}

	/**
	 * Devuelve la hora que coincida con los valores que recibe por parámetros.
	 * La hora deberá tener fecha de baja <code>null</code> o bien ser posterior
	 * a la fecha actual. Si el identificador que recibe es <code>null</code> no
	 * se filtrará por el identificador.
	 * 
	 * @param id
	 *            Identificador de la hora
	 * @param horaEntrada
	 *            Hora de entrada
	 * @param minutoEntrada
	 *            Minuto de entrada
	 * @param horaSalida
	 *            Hora de salida
	 * @param minutoSalida
	 *            Minuto de salida
	 * @return Hora recuparada o <code>null</code> si no se ha encontrado una
	 *         hora que cumpla con los criterios
	 */
	@SuppressWarnings("unchecked")
	public Hora obtenerHora(Integer id, int horaEntrada, int minutoEntrada, int horaSalida, int minutoSalida) {
		if (log.isDebugEnabled()) {
			log.debug("HorarioService.obtenerHora(id:" + id + ", horaEntrada:" + horaEntrada + ", minutoEntrada:" + minutoEntrada + ", horaSalida:"
					+ horaSalida + ", minutoSalida:" + minutoSalida + ")");
		}
		StringBuffer queryString = new StringBuffer();
		queryString.append("from Hora h ");
		queryString.append("where h.horaEntrada = :he ");
		queryString.append("and h.minutoEntrada = :me ");
		queryString.append("and h.horaSalida = :hs ");
		queryString.append("and h.minutoSalida = :ms ");
		queryString.append("and (h.baja = null or h.baja > :currentDate)");
		if (id != null) {
			queryString.append(" and h.id != :id");
		}

		log.info(queryString);
		Query query = entityManager.createQuery(queryString.toString());
		query.setParameter("he", horaEntrada);
		query.setParameter("me", minutoEntrada);
		query.setParameter("hs", horaSalida);
		query.setParameter("ms", minutoSalida);
		query.setParameter("currentDate", new Date());
		if (id != null) {
			query.setParameter("id", id);
		}
		Hora hora = null;
		List<Hora> horas = query.getResultList();
		if (horas != null && !horas.isEmpty()) {
			hora = horas.get(0);
		}
		return hora;
	}

	/**
	 * Devuelve un listado de casillas que estén contenidas en las horas que
	 * recibe por parámetros.
	 * 
	 * @param horas
	 *            Listado de horas
	 * @return Listado de casillas encontradas
	 */
	@SuppressWarnings("unchecked")
	public List<Casilla> obtenerCasillas(List<Hora> horas) {
		if (log.isDebugEnabled()) {
			log.debug("HorarioService.obtenerCasillas(horas:" + horas + ")");
		}
		String query = "from Casilla c where c.hora in (:horas) and (c.baja is null or c.baja > :currentDate) order by c.dia";
		log.info(query);
		return entityManager.createQuery(query).setParameter("horas", horas).setParameter("currentDate", new Date()).getResultList();
	}

	/**
	 * Devuelve un listado con los grupos que están activos, es decir, los
	 * grupos cuya fecha de baja es <code>null</code> o posterior a la fecha
	 * actual. El listado estará ordenado por nombre del grupo.
	 * 
	 * @return Listado de grupos activos
	 */
	@SuppressWarnings("unchecked")
	public List<Grupo> obtenerGruposActivos() {
		if (log.isDebugEnabled()) {
			log.debug("HorarioService.obtenerGruposActivos()");
		}
		String query = "from Grupo g where g.baja is null or g.baja > :currentDate order by g.nombre";
		log.info(query);
		return entityManager.createQuery(query).setParameter("currentDate", new Date()).getResultList();
	}

	/**
	 * Devuelve un listado de grupos ordenados por nombre.
	 * 
	 * @return Listado de grupos
	 */
	@SuppressWarnings("unchecked")
	public List<Grupo> obtenerGrupos() {
		if (log.isDebugEnabled()) {
			log.debug("HorarioService.obtenerGruposActivos()");
		}
		String query = "from Grupo g order by g.nombre";
		log.info(query);
		return entityManager.createQuery(query).getResultList();
	}

	/**
	 * Inserta o modifica la casilla que recibe por parámetros. Si la casilla no
	 * existe, se le asignará la fecha actual como fecha de creación.
	 * 
	 * @param casilla
	 *            Casilla a insertar/modificar
	 */
	public void insertarModificarCasilla(Casilla casilla) {
		if (log.isDebugEnabled()) {
			log.debug("HorarioService.insertarModificarCasilla(casilla:" + casilla + ")");
		}
		if (casilla != null) {
			Date hoy = new Date();
			if (casilla.getId() == null) {
				casilla.setCreado(hoy);
			}
			casilla.setModificado(hoy);
			entityManager.persist(casilla);
		}
		entityManager.flush();
	}

	/**
	 * Inserta y modifica la hora que recibe por parámetros.
	 * 
	 * @param hora
	 *            Hora a insertar/modificar
	 */
	@Transactional
	public void insertarModificarHora(Hora hora) {
		if (log.isDebugEnabled()) {
			log.debug("HorarioService.insertarModificarHora(hora:" + hora + ")");
		}
		if (hora != null) {
			Date hoy = new Date();
			if (hora.getId() == null) {
				hora.setCreado(hoy);
			}
			hora.setModificado(hoy);
			entityManager.persist(hora);
		}
		entityManager.flush();
	}

	/**
	 * Asigna la fecha actual como fecha de baja a la hora que recibe por
	 * parámetros.
	 * 
	 * @param hora
	 *            Hora a cancelar
	 */
	@Transactional
	public void cancelarHora(Hora hora) {
		if (log.isDebugEnabled()) {
			log.debug("HorarioService.cancelarHora(hora:" + hora + ")");
		}
		if (hora != null && hora.getId() != null) {
			Date hoy = new Date();
			hora.setModificado(hoy);
			hora.setBaja(hoy);
			entityManager.persist(hora);
			if (hora.getCasillas() != null) {
				for (Casilla casilla : hora.getCasillas()) {
					casilla.setModificado(hoy);
					casilla.setBaja(hoy);
					entityManager.persist(casilla);
				}
			}
		}
		entityManager.flush();
	}
}
